#include "matrix.hpp"
#include "matrix.hpp" // 包含多次，测试头文件对重复包含的保护机制。

// 用来测试输出算符
#include <sstream>

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest.h>

// 不得暴露std命名空间。

// 数组元素个数
#define elem_of(a) (sizeof(a) / sizeof(a[0]))

TEST_CASE_TEMPLATE("matrix::ctor", T, double, float) {
  // 所实现的矩阵类应当只有3个成员，大小与类型T无关。
  CHECK_EQ(sizeof(sdu::Matrix<T>), 8 * 3);
  // 类模板的默认类型实参为`double`。
  CHECK_EQ(typeid(sdu::Matrix<>::value_type), typeid(double));

  { // 在栈上分配较多的局部对象，测试默认构造函数的成员初始化。
    const int K = 1050;
    const sdu::Matrix<T> m[K];
    int err = 0;
    for(int i = 0; i < K; ++i) {
      if(!m[i].is_empty()) {
        ++err;
      }
    }
    CHECK_EQ(err, 0);
  }
  { // 尺寸参数应为无符号数
    const sdu::Matrix<T> m(-18446744073709551610ULL, -18446744073709551615ULL);
    CHECK_FALSE(m.is_empty()); // 非空
    CHECK_GT(m.row(), 0);
    CHECK_GT(m.col(), 0);
  }
  { // 指定尺寸和初值的构造函数
    const sdu::Matrix<T> m(10, 20); // 默认实参为nullptr
    CHECK_EQ(m.row(), 10); // 测试行数、列数查询
    CHECK_EQ(m.col(), 20);
  }
  { // 初值过少
    const T a[] = {1, 2};
    sdu::Matrix<T> m(1, 10, a, a + elem_of(a));
    CHECK_EQ(m.row(), 1);
    CHECK_EQ(m.col(), 10);
    CHECK_EQ(m[0][0], 1); // 测试元素获取功能
    CHECK_EQ(m[0][1], 2);
    m[0][1] = 3;
    CHECK_EQ(m[0][1], 3); // 测试元素赋值功能
    for(int c = 2; c < 10; ++c) {
      CHECK_EQ(m[0][c], T()); // 剩余元素默认初始化
    }
  }
  { // 初值过多
    const T a[] = {1, 2, 3, 4, 5};
    const sdu::Matrix<T> m(3, 1, a, a + elem_of(a));
    CHECK_EQ(m.row(), 3);
    CHECK_EQ(m.col(), 1);
    CHECK_EQ(m[0][0], 1);
    CHECK_EQ(m[1][0], 2);
    CHECK_EQ(m[2][0], 3);
  }
  { // 方阵构造函数
    const sdu::Matrix<T> m(4);
    CHECK_EQ(m.row(), 4);
    CHECK_EQ(m.col(), 4);
  }
  { // 单位阵
    const auto m = sdu::Matrix<T>::identity(2);
    CHECK_EQ(m.row(), 2);
    CHECK_EQ(m.col(), 2);
    CHECK_EQ(m[0][0], 1);
    CHECK_EQ(m[0][1], 0);
    CHECK_EQ(m[1][0], 0);
    CHECK_EQ(m[1][1], 1);
  }
  { // 拷贝构造
    const T a[] = {1, 2, 3};
    const sdu::Matrix<T> ma(3, 2, a, a + elem_of(a));
    const sdu::Matrix<T> mb(ma);
    CHECK_EQ(mb.row(), 3);
    CHECK_EQ(mb.col(), 2);
    CHECK_EQ(mb[0][0], 1); // 两边值类型不同，用CHECK_EQ不好写。
    CHECK_EQ(mb[0][1], 2);
    CHECK_EQ(mb[1][0], 3);
    CHECK_EQ(mb[1][1], 0);

    CHECK_THROWS(mb[0][2]); // 越界

    // 拷贝赋值算符
    sdu::Matrix<T> mc;
    CHECK(mc.is_empty()); // 默认为空

    mc = mb; // 右侧参数应允许常量
    CHECK(!mc.is_empty());
    CHECK_EQ(mc.row(), 3); // 同样大小
    CHECK_EQ(mc.col(), 2);
    CHECK_EQ(mc[0][0], 1); // 同样内容
    CHECK_EQ(mc[0][1], 2);
    CHECK_EQ(mc[1][0], 3);
    CHECK_EQ(mc[1][1], 0);
  }
  { // 移动构造
    const T a[] = {1, 2, 3};
    sdu::Matrix<T> ma(3, 2, a, a + elem_of(a));
    CHECK(!ma.is_empty());

    sdu::Matrix<T> mb(std::move(ma)); // 要求移动构造
    CHECK(ma.is_empty()); // 移动后ma为空
    CHECK_EQ(mb.row(), 3);
    CHECK_EQ(mb.col(), 2);
    CHECK_EQ(mb[0][0], 1);
    CHECK_EQ(mb[0][1], 2);
    CHECK_EQ(mb[1][0], 3);
    CHECK_EQ(mb[1][1], 0);

    sdu::Matrix<T> mc;
    CHECK(mc.is_empty()); // 默认为空

    mc = std::move(mb); // 移动赋值
    CHECK(mb.is_empty()); // 移动后mb为空
    CHECK(!mc.is_empty());

    CHECK_EQ(mc.row(), 3); // 同样大小
    CHECK_EQ(mc.col(), 2);
    CHECK_EQ(mc[0][0], 1); // 同样内容
    CHECK_EQ(mc[0][1], 2);
    CHECK_EQ(mc[1][0], 3);
    CHECK_EQ(mc[1][1], 0);
  }
}

TEST_CASE_TEMPLATE("matrix::dtor", T, double, float) {
  // 析构函数
}

TEST_CASE_TEMPLATE("matrix::swap", T, double, float) {
  const T ea[] = {1, 2, 3, 4};
  const T eb[] = {5, 6, 7, 8, 9};
  sdu::Matrix<T> a(2, 3, ea, ea + elem_of(ea));
  sdu::Matrix<T> b(4, 1, eb, eb + elem_of(eb)); // 形状不同
  a.swap(b);
  CHECK_EQ(a.row(), 4); // 尺寸交换
  CHECK_EQ(a.col(), 1);
  CHECK_EQ(a[0][0], 5); // 数据交换
  CHECK_EQ(a[1][0], 6);
  CHECK_EQ(a[2][0], 7);
  CHECK_EQ(a[3][0], 8);

  CHECK_EQ(b.row(), 2);
  CHECK_EQ(b.col(), 3);
  CHECK_EQ(b[0][0], 1);
  CHECK_EQ(b[0][1], 2);
  CHECK_EQ(b[0][2], 3);
  CHECK_EQ(b[1][0], 4);
  CHECK_EQ(b[1][1], 0);
  CHECK_EQ(b[1][2], 0);
}

TEST_CASE_TEMPLATE("matrix::assign", T, double, float) {
  { // 自赋值的情况。
    const T ea[] = {1, 2, 3, 4};
    sdu::Matrix<T> m(2, 3, ea, ea + elem_of(ea));

    m = m;
    CHECK_EQ(m.row(), 2);
    CHECK_EQ(m.col(), 3);

    m = std::move(m);
    CHECK_EQ(m.row(), 2);
    CHECK_EQ(m.col(), 3);
    CHECK_EQ(m[0][0], 1);
    CHECK_EQ(m[0][1], 2);
    CHECK_EQ(m[0][2], 3);
    CHECK_EQ(m[1][0], 4);
  }
}

TEST_CASE_TEMPLATE("matrix::move", T, double, float) {
  // 测试移动语义
  constexpr size_t L = 4096;
  sdu::Matrix<> a[L]; // 矩阵数组，全默认为空矩阵。
  a[0].resize(L, L); // 16M个元素 * 8 ~= 128MB字节。
  for(size_t i = 1; i < L; ++i) {
    // a[i] = a[i-1]; // 拷贝赋值会分配4K个 * 128MB ~ 512GB内存
    a[i] = std::move(a[i-1]); // 只有移动语义才能通过这个测试。
  }
  CHECK_EQ(a[L-1].row(), L);
  CHECK_EQ(a[L-1].col(), L);
}

TEST_CASE_TEMPLATE("matrix::scalar_product", T, double, float) {
  // 测试数乘
  const T ea[] = {1, 2, 3};
  sdu::Matrix<T> m(2, 2, ea, ea + elem_of(ea));
  m *= 2;
  CHECK_EQ(m[0][0], 2);
  CHECK_EQ(m[0][1], 4);
  CHECK_EQ(m[1][0], 6);
  CHECK_EQ(m[1][1], 0);

  const auto mb = m * 3;
  CHECK_EQ(mb[0][0], 6);
  CHECK_EQ(mb[0][1], 12);
  CHECK_EQ(mb[1][0], 18);
  CHECK_EQ(mb[1][1], 0);
}

TEST_CASE_TEMPLATE("matrix::matrix_product", T, double, float) {
  // 测试矩阵乘法
  const T ea[] = {1, 2, 3, 4, 5, 6};
  const T eb[] = {7, 8, 9};
  const sdu::Matrix<T> A(2, 3, ea, ea + elem_of(ea));
  const sdu::Matrix<T> B(3, 1, eb, eb + elem_of(eb));
  const auto C = A * B;
  CHECK_EQ(C.row(), 2);
  CHECK_EQ(C.col(), 1);
  CHECK_EQ(C[0][0], 7 + 16 + 27);
  CHECK_EQ(C[1][0], 28 + 40 + 54);
}

TEST_CASE_TEMPLATE("matrix::output", T, double, float) {
  // 测试输出
  const T ea[] = {1, 2, 3, 4, 5, 6};
  const sdu::Matrix<T> A(2, 3, ea, ea + elem_of(ea));
  std::ostringstream oss;
  oss << A;
  const std::string expect= R"([
1 2 3
4 5 6
])";

  CHECK_EQ(oss.str(), expect);
}